﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Core.Models;
using System.Web.Security;
using OliveLite.Core.Membership;
using System.Security.Principal;
using System.Web.Mvc.Html;

namespace Core.Controllers
{
    [Authorize]
    public class UserController : Controller
    {
        //
        // GET: /User/

        public ActionResult Index(Guid id)
        {
            CoreEntities core = new CoreEntities();
            ViewData["BusinessUnitName"] = core.BusinessUnits.First(m => m.BusinessUnitId == id).BusinessUnitName;
            ViewData["BusinessUnitId"] = id;
            return View();
        }

        [HttpPost]
        public JsonResult UserList(FormCollection collection)
        {
            CoreEntities core = new CoreEntities();
            Guid BUId = new Guid(collection.GetValues("BusinessUnitId")[0].ToString());
            int SkipRecs = int.Parse(collection.GetValues("iDisplayStart")[0].ToString());
            int TakeCount = int.Parse(collection.GetValues("iDisplayLength")[0].ToString());
            string strSearch = collection.GetValues("sSearch")[0].ToString();
            DataTableResponse r = new DataTableResponse();
            // Needs update when paging & sorting is implemented
            List<User> users = core.Users.Where(m => ((m.Contact.FullName.Contains(strSearch) || m.Contact.Email.Contains(strSearch)) && m.BusinessUnitId == BUId && m.Contact.IsDroppedYN == "N" && m.IsDroppedYN == "N")).OrderBy(m => m.Contact.FullName).Skip(SkipRecs).Take(TakeCount).ToList();
            r.sEcho = collection.GetValues("sEcho")[0].ToString();
            // This query is required because the earlier query returns results for only 1 page.
            r.iTotalRecords = core.Users.Where(m => ((m.Contact.FullName.Contains(strSearch) || m.Contact.Email.Contains(strSearch)) && m.BusinessUnitId == BUId && m.Contact.IsDroppedYN == "N" && m.IsDroppedYN == "N")).Count();
            r.iTotalDisplayRecords = r.iTotalRecords;
            foreach (User user in users)
            {
                Core.Models.ContactUser cu = user.Contact.ContactUsers.First(m => m.ContactId == user.ContactId);
                List<string> r1 = new List<string>();
                string editurl = String.Format("<a href=\"/{0}/{1}/{2}\" id=\"editlink\" title=\"{1}\" >Edit</a>", "User", "Edit", user.UserId.ToString());
                string detailsurl = String.Format("<a href=\"/{0}/{1}/{2}\" id=\"detailslink\" title=\"{1}\" >Details</a>", "User", "Details", user.UserId.ToString());
                string deleteurl = String.Format("<a href=\"/{0}/{1}/{2}\" id=\"deletelink\" title=\"{1}\" >Delete</a>", "User", "Delete", user.UserId.ToString());
                r1.Add(editurl + detailsurl + deleteurl);
                r1.Add(user.Contact.FullName);
                r1.Add(user.Contact.Email);
                r1.Add(Core.Tools.YesNo(cu.IsApprovedYN));
                r1.Add(Core.Tools.YesNo(cu.IsLockedOutYN));
                r1.Add(String.Format("{0:g}", cu.LastActivityDT));
/*                r1.Add(String.Format("{0:g}", user.Contact.ModifiedDT)); */
                r.aaData.Add(r1);
            }
            return Json(r);
        }

        //
        // GET: /User/Details/5

        public ActionResult Details(Guid id)
        {
            CoreEntities core = new CoreEntities();
            User user = core.Users.First(m => m.UserId == id);
            return View(user);
        }

        //
        // GET: /User/Create

        public ActionResult Create(Guid id)
        {
            UserUI u = new UserUI();
            u.UserId = Guid.Empty;
            u.BusinessUnitId = id;
            return View(u);
        } 

        //
        // POST: /User/Create

        [HttpPost]
        public ActionResult Create(UserUI model)
        {
            // IMembershipService MembershipService = new AccountMembershipService();
            CoreEntities core = new CoreEntities();
            if (ModelState.IsValid)
            {
                //Check if user already exists
                BusinessUnit bu = core.BusinessUnits.First(m => m.BusinessUnitId == model.BusinessUnitId);
                if (core.Contacts.Where(m => m.Email == model.Email).Count() > 0)
                {
                    // Process exisiting User                    
                    bool sendinvite = false;
                    bool savechanges = false;
                    bool sendnotification = false;
                    Contact c = core.Contacts.First(m => m.Email == model.Email);
                    // User may be dropped in which case revive and Invite again
                    if (c.IsDroppedYN == "Y")
                    {
                        c.IsDroppedYN = "N";
                        savechanges = true;
                        sendinvite = true;
                    }
                    if (c.ContactBUs.Where(m => m.BusinessUnitId == model.BusinessUnitId).Count() <= 0)
                    {
                        ContactBU cbu = new ContactBU();
                        cbu.ContactBusinessUnitId = Guid.NewGuid();
                        cbu.BusinessUnitId = model.BusinessUnitId;
                        cbu.ContactId = c.ContactId;
                        cbu.OptIn = "Y"; // User Account is always OptedIn
                        cbu.OptInModifiedDT = DateTime.Now;
                        c.ContactBUs.Add(cbu);
                        savechanges = true;
                    }
                    /*
                    if (c.BusinessUnits.Where(m => m.BusinessUnitId == model.BusinessUnitId).Count() <= 0)
                    {
                        c.BusinessUnits.Add(bu);
                        savechanges = true;
                    }
                    */
                    // Create an User Account if contact was never an user on the system
                    if (core.ContactUsers.Where(m => m.ContactId == c.ContactId).Count() <= 0)
                    {
                        ContactUser cu = CreateContactUser(ref core, ref c);
                        User u = CreateUser(model, ref core, c);
                        UpdateRoles(ref core, u.UserId, model.RoleIds);
                        savechanges = true;
                        sendinvite = true;
                    }
                    else
                    {
                        ContactUser cu = core.ContactUsers.First(m => m.ContactId == c.ContactId);
                        // Create a User BusinessUnit association if user was never on this business unit
                        if (core.Users.Where(m => (m.ContactId == c.ContactId && m.BusinessUnitId == model.BusinessUnitId)).Count() <= 0)
                        {
                            User u = CreateUser(model, ref core, c);
                            UpdateRoles(ref core, u.UserId, model.RoleIds);
                            savechanges = true;
                            sendnotification = true;
                        }
                        else
                        {
                            // Check to make sure that account is not deleted for this business unit
                            Core.Models.User u = core.Users.First(m => (m.ContactId == c.ContactId && m.BusinessUnitId == model.BusinessUnitId));
                            if (u.IsDroppedYN == "Y")
                            {
                                u.IsDroppedYN = "N";
                                savechanges = true;
                                sendnotification = true;
                            }
                        }
                        if (cu.IsDroppedYN == "Y")
                        {
                            cu.IsDroppedYN = "N";
                            savechanges = true;
                            sendinvite = true;
                        }
                        if (cu.IsApprovedYN == "N")
                        {
                            sendinvite = true;
                        }
                    }
                    if (sendinvite)
                    {
                        ContactUser cu = core.ContactUsers.First(m => m.ContactId == c.ContactId);
                        cu.InviteCode = Core.Tools.RandomString(10, false);
                        cu.InviteExpires = DateTime.Now.AddHours(6);
                        cu.IsApprovedYN = "N";
                        cu.IsLockedOutYN = "Y";
                        savechanges = true;
                        string emailbody = "Welcome! " +
                            "You are invited to create an account for " + bu.BusinessUnitName + ".<br /><br />" +
                            "To create the account please visit http://" + Session["DomainName"].ToString() + "/Account/Register <br /><br />" +
                            "You will need the email address where you received this notification and a security code. Your security code is:<br /><br />" +
                            cu.InviteCode + "<br /><br />" +
                            "Please note that for security purposes the above code will expire in approximately 6 hrs.<br /><br />" +
                            "If you think you have received this in error, please ignore this email<br />Thank you.";
                        string subject = "Account creation for " + bu.BusinessUnitName;
                        List<EmailAddress> to = new List<EmailAddress>();
                        to.Add(new EmailAddress((c.FirstName + (String.IsNullOrEmpty(c.LastName) ? "" : " " + c.LastName)), c.Email));
                        Core.Tools.SendMail((Guid)Session["DomainId"], subject, emailbody, to);
                    }
                    if (savechanges) core.SaveChanges();
                    if (sendnotification)
                    {
                        string emailbody = "Welcome! " +
                            "You have been provided access for " + bu.BusinessUnitName + ".<br /><br />" +
                            "You should be able to login at  http://" + Session["DomainName"].ToString() + " <br /><br />" +
                            "Thank you.";
                        string subject = "Account activated for " + bu.BusinessUnitName;
                        List<EmailAddress> to = new List<EmailAddress>();
                        to.Add(new EmailAddress((c.FirstName + (String.IsNullOrEmpty(c.LastName) ? "" : " " + c.LastName)), c.Email));
                        Core.Tools.SendMail((Guid)Session["DomainId"], subject, emailbody, to);
                    }
                    if ((!sendinvite) && (!sendnotification))
                    {
                        ModelState.AddModelError("", "User already exists.");
                        UpdateRolesinModel(ref model, core);
                        return View(model);
                    }
                    else
                    {
                        return RedirectToAction("Index", new { id = bu.BusinessUnitId });
                    }                    
                }
                else
                {
                    // Create Contact
                    Contact c = new Contact();
                    c.ContactId = Guid.NewGuid();                    
                    c.FirstName = model.FirstName;
                    c.LastName = model.LastName;
                    c.Email = model.Email;
                    c.EmailVerifiedYN = "N";
                    c.Status = "A";
                    c.IsDroppedYN = "N";
                    c.CreatedDT = DateTime.Now;
                    c.CreatedById = (Guid)Session["UserId"];
                    c.CreatedByName = Session["UserFullName"].ToString();
                    c.ModifiedDT = DateTime.Now;
                    c.ModifiedById = (Guid)Session["UserId"];
                    c.ModifiedByName = Session["UserFullName"].ToString();
                    ContactBU cbu = new ContactBU();
                    cbu.ContactBusinessUnitId = Guid.NewGuid();
                    cbu.ContactId = c.ContactId;
                    cbu.BusinessUnitId = model.BusinessUnitId;
                    cbu.OptIn = "Y";
                    cbu.OptInModifiedDT = DateTime.Now;
                    c.ContactBUs.Add(cbu);
                    //c.BusinessUnits.Add(core.BusinessUnits.First(b => b.BusinessUnitId == model.BusinessUnitId));
                    core.AddToContacts(c);
                    ContactUser cu = CreateContactUser(ref core, ref c);
                    User u = CreateUser(model, ref core, c);
                    // Create Roles
                    UpdateRoles(ref core, u.UserId, model.RoleIds);
                    core.SaveChanges();
                    string emailbody = "Welcome! " +
                        "You are invited to create an account for " + bu.BusinessUnitName + ".<br /><br />" +
                        "To create the account please visit http://" + Session["DomainName"].ToString() + "/Account/Register <br /><br />" +
                        "You will need the email address where you received this notification and a security code. Your security code is:<br /><br />" +
                        cu.InviteCode + "<br /><br />" +
                        "Please note that for security purposes the above code will expire in approximately 6 hrs.<br /><br />" +
                        "If you think you have received this in error, please ignore this email<br />Thank you.";
                    string subject = "Account creation for " + bu.BusinessUnitName;
                    List<EmailAddress> to = new List<EmailAddress>();
                    to.Add(new EmailAddress((c.FirstName + (String.IsNullOrEmpty(c.LastName) ? "" : " " + c.LastName)),c.Email));
                    Core.Tools.SendMail((Guid)Session["DomainId"], subject, emailbody, to);
                    return RedirectToAction("Index", new { id = bu.BusinessUnitId});
                }
            }

            // If we got this far, something failed, redisplay form
            // ViewBag.PasswordLength = MembershipService.MinPasswordLength;
            UpdateRolesinModel(ref model, core);
            return View(model);
        }

        private void UpdateRolesinModel(ref UserUI model, CoreEntities core)
        {
            List<string> roles = model.RoleIds.Split((",").ToCharArray(), 9999).ToList();
            List<Guid> RoleIds = new List<Guid>();
            List<Role> dbroles = core.Roles.Where(m => (m.IsDroppedYN == "N" || m.IsDroppedYN == "Y")).ToList();
            string RoleDisplay = "";
            foreach (string roleid in roles)
            {
                if (!String.IsNullOrEmpty(roleid))
                {
                    Guid role = new Guid(roleid);
                    RoleIds.Add(role);
                    Role r = dbroles.First(d => d.RoleId.Equals(role));
                    RoleDisplay += "&nbsp;&lt;<span>" + r.Name + "&nbsp;<a href='javascript: RemoveRole(\"" + r.RoleId.ToString() + "\")'>X</a></span>&gt;";
                }
            }
            model.RoleDisplay = RoleDisplay;
        }

        private User CreateUser(UserUI model, ref CoreEntities core, Contact c)
        {
            // Create User
            User u = new User();
            u.UserId = Guid.NewGuid();
            u.ContactId = c.ContactId;
            u.BusinessUnitId = model.BusinessUnitId;
            u.IsDroppedYN = "N";
            u.CreateDT = DateTime.Now;
            u.CreatedById = (Guid)Session["UserId"];
            u.CreatedByName = Session["UserFullName"].ToString();
            u.ModifiedDT = DateTime.Now;
            u.ModifiedById = (Guid)Session["UserId"];
            u.ModifiedByName = Session["UserFullName"].ToString();
            core.AddToUsers(u);
            return u;
        }

        private static ContactUser CreateContactUser(ref CoreEntities core, ref Contact c)
        {
            // Create ContactUser
            c.Status = "A";
            ContactUser cu = new ContactUser();
            cu.ContactId = c.ContactId;
            cu.OpenIDUserName = c.Email;
            cu.Password = Core.Tools.RandomString(10, false);
            cu.InviteCode = Core.Tools.RandomString(10, false);
            cu.InviteExpires = DateTime.Now.AddHours(6);
            cu.IsDroppedYN = "N";
            cu.IsApprovedYN = "N";
            cu.IsLockedOutYN = "Y";
            cu.IsOnLineYN = "N";
            cu.CreateDT = DateTime.Now;
            cu.FailedPasswordAttemptCount = 0;
            cu.FailedPasswordAnswerAttemptCount = 0;
            core.AddToContactUsers(cu);
            return cu;
        }
        
        //
        // GET: /User/Edit/5
 
        public ActionResult Edit(Guid id)
        {
            CoreEntities core = new CoreEntities();
            User user = core.Users.First(m => m.UserId == id);
            UserUI userui = new UserUI();
            userui.FirstName = user.Contact.FirstName;
            userui.LastName = user.Contact.LastName;
            userui.Email = user.Contact.Email;
            userui.BusinessUnitId = user.BusinessUnitId;
            string RoleIds = "";
            string RoleDisplay = "";
            foreach (Authorization auth in user.Authorizations.Where(m => (m.UserId == id && m.IsDroppedYN == "N" && m.RoleId != null)))
            {
                RoleIds += "," + auth.Role.RoleId.ToString();
                /* if the formatting below is changed, the changes need to be cascaded to the edit & create views as well. */
                RoleDisplay += "&nbsp;&lt;<span>" + auth.Role.Name + "&nbsp;<a href='javascript: RemoveRole(\"" + auth.RoleId.ToString() + "\")'>X</a></span>&gt;";
            }
            userui.RoleIds = RoleIds;
            userui.RoleDisplay = RoleDisplay;
            return View(userui);
        }

        //
        // POST: /User/Edit/5

        [HttpPost]
        public ActionResult Edit(Guid id, FormCollection collection)
        {
            try
            {
                // TODO: Add update logic here
                if (ModelState.IsValid)
                {
                    CoreEntities core = new CoreEntities();
                    User u = core.Users.First(m => m.UserId == id);
                    Contact contact = core.Contacts.First(m => m.ContactId == u.ContactId);
                    UpdateModel(contact, collection.ToValueProvider());
                    contact.ModifiedDT = DateTime.Now;
                    contact.ModifiedById = (Guid)Session["UserId"];
                    contact.ModifiedByName = Session["UserFullName"].ToString();
                    UpdateRoles(ref core, id, collection.GetValues("RoleIds")[0].ToString());
                    core.SaveChanges();
                    return RedirectToAction("Index", new { id = u.BusinessUnitId});
                }
                else
                    return View();
            }
            catch(Exception e)
            {
                ModelState.AddModelError("", Tools.ExceptionMessage(e));
                return View();
            }
        }

        //
        // GET: /User/Delete/5
 
        public ActionResult Delete(Guid id)
        {
            CoreEntities core = new CoreEntities();
            User user = core.Users.First(m => m.UserId == id);
            return View(user);
        }

        //
        // POST: /User/Delete/5

        [HttpPost]
        public ActionResult Delete(Guid id, FormCollection collection)
        {
            try
            {
                // TODO: Add delete logic here
                if (ModelState.IsValid)
                {
                    bool removecontactuser = false;
                    CoreEntities core = new CoreEntities();
                    User t = core.Users.First(b => b.UserId == id);
                    if (core.Users.Where(m => (m.ContactId == t.ContactId && m.IsDroppedYN == "N")).Count() <= 1) removecontactuser = true;
                    t.IsDroppedYN = "Y";
                    t.ModifiedDT = DateTime.Now;
                    t.ModifiedById = (Guid)Session["UserId"];
                    t.ModifiedByName = Session["UserFullName"].ToString();
                    if (removecontactuser)
                    {
                        ContactUser cu = core.ContactUsers.First(m => m.ContactId == t.ContactId);
                        cu.IsDroppedYN = "Y";
                        cu.IsApprovedYN = "N";
                        cu.IsLockedOutYN = "Y";
                    }
                    core.SaveChanges();
                    return RedirectToAction("Index", new { id = t.BusinessUnitId });
                }
                else
                    return View();
            }
            catch(Exception e)
            {
                ModelState.AddModelError("", Tools.ExceptionMessage(e));
                return View();
            }
        }

        public ActionResult Reset(Guid id)
        {
            CoreEntities core = new CoreEntities();
            User user = core.Users.First(m => m.UserId == id);
            return View(user);
        }

        [HttpPost]
        public ActionResult Reset(FormCollection collection)
        {
            Guid UserId = new Guid(collection.GetValues("UserId")[0].ToString());
            CoreEntities core = new CoreEntities();
            User u = core.Users.First(m => m.UserId == UserId);
            Contact c = u.Contact;
            BusinessUnit bu = u.BusinessUnit;
            ContactUser cu = core.ContactUsers.First(m => m.ContactId == c.ContactId);
            cu.InviteCode = Core.Tools.RandomString(10, false);
            cu.InviteExpires = DateTime.Now.AddHours(6);
            cu.IsApprovedYN = "N";
            cu.IsLockedOutYN = "Y";
            core.SaveChanges();
            string emailbody = "Welcome! " +
                "You are requested to re-register your account for " + bu.BusinessUnitName + ".<br /><br />" +
                "To create the account please visit http://" + Session["DomainName"].ToString() + "/Account/Register <br /><br />" +
                "You will need the email address where you received this notification and a security code. Your security code is:<br /><br />" +
                cu.InviteCode + "<br /><br />" +
                "Please note that for security purposes the above code will expire in approximately 6 hrs.<br /><br />" +
                "If you think you have received this in error, please ignore this email<br />Thank you.";
            string subject = "Account creation for " + bu.BusinessUnitName;
            List<EmailAddress> to = new List<EmailAddress>();
            to.Add(new EmailAddress((c.FirstName + (String.IsNullOrEmpty(c.LastName) ? "" : " " + c.LastName)), c.Email));
            Core.Tools.SendMail((Guid)Session["DomainId"], subject, emailbody, to);
            return RedirectToAction("Index", new { id = u.BusinessUnitId });
        }
        
        [HttpPost]
        public JsonResult Roles(string search, int maxitems)
        {
            CoreEntities core = new CoreEntities();
            List<Role> roles = core.Roles.Where(m => m.Name.Contains(search)).OrderBy(m => m.Name).Take(maxitems).ToList();
            List<NameId> data = new List<NameId>();
            foreach(Role role in roles)
            {
                NameId item = new NameId();
                item.Name = role.Name;
                item.Id = role.RoleId.ToString();
                data.Add(item);
            }
            return Json(data);
        }

        private void UpdateRoles(ref CoreEntities core, Guid userid, string roleids)
        {
            List<string> roles = roleids.Split((",").ToCharArray(),9999).ToList();
            List<Guid> RoleIds = new List<Guid>();
            foreach (string roleid in roles)
                if (!String.IsNullOrEmpty(roleid)) RoleIds.Add(new Guid(roleid));
            List<Authorization> auths = core.Authorizations.Where(m => (m.UserId == userid && m.RoleId != null)).ToList();
            /* Remove the roles that are in the DB but removed by administrator */
            foreach (Authorization auth in auths)
            {
                if (!RoleIds.Exists(m => m.Equals(auth.RoleId)))
                {
                    auth.IsDroppedYN = "Y";
                    /* Add audit entry for Role removal here */
                }
            }
            /* Add the roles that do not exist */
            foreach(Guid RoleId in RoleIds)
            {
                if(auths.Exists(m => m.RoleId == RoleId))
                {
                    Authorization auth = auths.First(m => m.RoleId == RoleId);
                    if (auth.IsDroppedYN == "Y")
                    {
                        auth.IsDroppedYN = "N";
                        /* Add audit entry for Role add here */
                    }
                }
                else
                {
                    Authorization auth = new Authorization();
                    auth.AuthorizationId = Guid.NewGuid();
                    auth.UserId = userid;
                    auth.RoleId = RoleId;
                    auth.IsDroppedYN = "N";
                    auth.CreateDT = DateTime.Now;
                    auth.CreatedById = (Guid)Session["UserId"];
                    auth.CreatedByName = Session["UserFullName"].ToString();
                    auth.ModifiedDT = DateTime.Now;
                    auth.ModifiedById = (Guid)Session["UserId"];
                    auth.ModifiedByName = Session["UserFullName"].ToString();
                    core.AddToAuthorizations(auth);
                    /* Add audit entry for Role add here */
                }
            }
            /* Changes will be saved by the calling function 
            core.SaveChanges(); */
        }
    }

}
